/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package test.alan.hyperblobs;

import android.graphics.Color;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.os.SystemClock;
import android.util.Log;

import java.util.*;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 *
 * @author Michael Zhang
 */
public class LevelDrawer implements GLSurfaceView.Renderer {

    /**
     * The data that is currently being drawn onto the screen.
     */
    private Map<String, Object> mDrawingData;
    /**
     * The data that will be drawn on the next {@code onDrawFrame()}.
     */
    private Map<String, Object> mNextData;
    /**
     * View Projection Matrix.
     */
    private final float[] mMVPMatrix = new float[16];
    /**
     * Projection Matrix.
     */
    private final float[] mProjMatrix = new float[16];
    /**
     * View Matrix.
     */
    private final float[] mVMatrix = new float[16];
    /**
     * Cached scratch Matrix to prevent constant allocation/deallocation of
     * arrays.
     */
    private final float[] mScrachMatrix = new float[16];
    /**
     * Our little testing triangle!
     */
    private GLTriangle mTriangle;
    /**
     * Our little testing rectangle!
     */
    private GLRectangle mRectangle;

    /**
     * Sets the data to be drawn on the next {@code onDrawFrame()}.
     *
     * @param data The data from {@code LevelUpdater}
     */
    @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
    void setData(Map<String, Object> data) {
        synchronized (this) {
            mNextData = data;
        }
    }

    /**
     *
     * @param gl
     * @param config
     */
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES20.glClearColor(0, 0, 0, 1);
        mTriangle = new GLTriangle();
        mTriangle.setPosition(240, 180, 150, 150);
        mTriangle.setColor(Color.CYAN);
        mRectangle = new GLRectangle();
        mRectangle.setPosition(240, 180, 100, 100);
        mRectangle.setColor(Color.MAGENTA);
    }

    /**
     *
     * @param gl
     * @param width
     * @param height
     */
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        float ratio = (float) width / height;
        Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
    }

    /**
     *
     * @param gl
     */
    public void onDrawFrame(GL10 gl) {
        synchronized (this) {
            mDrawingData = mNextData;
        }
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        Matrix.setLookAtM(mVMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);

        // Rotate the triangle
        long time = SystemClock.uptimeMillis() % 4000L;
        float angle = 0.090f * ((int) time);
        Matrix.setIdentityM(mScrachMatrix, 0);
        Matrix.setRotateM(mScrachMatrix, 0, angle, 0, 0, -1);
        Matrix.multiplyMM(mMVPMatrix, 0, mScrachMatrix, 0, mMVPMatrix, 0);
        // Draw the triangle
        mTriangle.draw(mMVPMatrix);
        
        // Reset the matrix
        Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0);

        // Rotate the rectangle
        angle = 0.090f * (4000 - (int) time);
        Matrix.setIdentityM(mScrachMatrix, 0);
        Matrix.setRotateM(mScrachMatrix, 0, angle, 0, 0, -1);
        Matrix.multiplyMM(mMVPMatrix, 0, mScrachMatrix, 0, mMVPMatrix, 0);
        // Draw the rectangle
        mRectangle.draw(mMVPMatrix);
    }

    //<editor-fold desc="Utility Methods">
    /**
     * Cheat-ish way of getting a typed value from a map.
     *
     * @param <T> The type to retrieve from the map
     * @param map The map
     * @param key The key for retrieval
     * @param cls The Class of the type
     * @return {@code map.get(key)} casted to {@code <T>}; {@code null} if the
     * cast failed
     */
    private static <T extends Object> T getFromMap(Map<String, Object> map, String key, Class<T> cls) {
        Object o = map.get(key);
        try {
            return cls.cast(o);
        } catch (ClassCastException cce) {
            Log.d(LevelActivity.NAME_LOGGING, "Casting Error:\n", cce);
        }
        return null;
    }

    /**
     * Loads an OpenGL Shader.
     *
     * @param type Shader type
     * @param shaderCode Shader code
     * @return The compiled shader
     */
    public static int loadShader(int type, String shaderCode) {

        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }
    //</editor-fold>
}
